package com.shm.leetcode;

import java.util.HashMap;
import java.util.Map;

/**
 * 697. 数组的度
 * 给定一个非空且只包含非负数的整数数组 nums，数组的度的定义是指数组里任一元素出现频数的最大值。
 *
 * 你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组，返回其长度。
 *
 *
 *
 * 示例 1：
 *
 * 输入：[1, 2, 2, 3, 1]
 * 输出：2
 * 解释：
 * 输入数组的度是2，因为元素1和2的出现频数最大，均为2.
 * 连续子数组里面拥有相同度的有如下所示:
 * [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
 * 最短连续子数组[2, 2]的长度为2，所以返回2.
 * 示例 2：
 *
 * 输入：[1,2,2,3,1,4,2]
 * 输出：6
 *
 *
 * 提示：
 *
 * nums.length 在1到 50,000 区间范围内。
 * nums[i] 是一个在 0 到 49,999 范围内的整数。
 * @author SHM
 */
public class FindShortestSubArray {
    /**
     * 给一个定义“度”：表示数组中任何相同元素最多了几次，换句话说就是你用一个哈希表去统计所有元素的出现次数，“度”就是整个哈希表取value的最大值。然后题目让你求达到这个值的 最小 连续子数组长度。
     *
     * 题目本身不难，简单的做法是先遍历一遍数组找到“度”，然后不断滑窗找到最小。代码如下：
     *
     * 作者：dong-zhu-lou-shou-ye-ren
     * 链接：https://leetcode-cn.com/problems/degree-of-an-array/solution/yi-ci-shu-zu-bian-li-ha-xi-biao-by-dong-d9lvg/
     * @param nums
     * @return
     */
    public int findShortestSubArray(int[] nums) {
        int n = nums.length;
        int left=0,right=0;
        HashMap<Integer,Integer> map = new HashMap<>();
        int count = 0;
        for(int i:nums){
            map.put(i,map.getOrDefault(i,0)+1);
            count = Math.max(count,map.get(i));
        }
        HashMap<Integer,Integer> map2 = new HashMap<>();
        int ans = Integer.MAX_VALUE;
        while(right<n){
            map2.put(nums[right],map2.getOrDefault(nums[right],0)+1);
            right++;

            while(map2.get(nums[right-1])==count){
                map2.put(nums[left],map2.get(nums[left])-1);
                ans=Math.min(ans,right-left);
                left++;
            }

        }
        return ans;
    }

    /**
     * 我还想到了一个一次遍历数组的解法。如果要一次遍历我们则需要记一下每个元素的第一次出现位置和当前出现了几次。我们可以把这两个变量封装为一个Pair。具体代码如下：
     *
     * 作者：dong-zhu-lou-shou-ye-ren
     * 链接：https://leetcode-cn.com/problems/degree-of-an-array/solution/yi-ci-shu-zu-bian-li-ha-xi-biao-by-dong-d9lvg/
     * @param nums
     * @return
     */
    public int findShortestSubArray_2(int[] nums) {
        Map<Integer,int[]> map = new HashMap<>();
        int maxCount=0,minWindow=0;
        for (int i = 0; i < nums.length; i++) {
            int[] ints = map.get(nums[i]);
            if(ints==null){
                ints = new int[]{i,1};
                map.put(nums[i],ints);
            }else {
                ints[1]++;
            }

            if(ints[1]>maxCount){
                maxCount = ints[1];
                minWindow = i-ints[0]+1;
            }else if(ints[1]==maxCount){
                minWindow = Math.min(minWindow,i-ints[0]+1);
            }
        }
        return minWindow;
    }

    /**
     * 方法一：哈希表
     * 思路及解法
     *
     * 记原数组中出现次数最多的数为 xx，那么和原数组的度相同的最短连续子数组，必然包含了原数组中的全部 xx，且两端恰为 xx 第一次出现和最后一次出现的位置。
     *
     * 因为符合条件的 xx 可能有多个，即多个不同的数在原数组中出现次数相同。所以为了找到这个子数组，我们需要统计每一个数出现的次数，同时还需要统计每一个数第一次出现和最后一次出现的位置。
     *
     * 在实际代码中，我们使用哈希表实现该功能，每一个数映射到一个长度为 33 的数组，数组中的三个元素分别代表这个数出现的次数、这个数在原数组中第一次出现的位置和这个数在原数组中最后一次出现的位置。当我们记录完所有信息后，我们需要遍历该哈希表，找到元素出现次数最多，且前后位置差最小的数。
     *
     * 复杂度分析
     *
     * 时间复杂度：O(n)O(n)，其中 nn 是原数组的长度，我们需要遍历原数组和哈希表各一次，它们的大小均为 O(n)O(n)。
     *
     * 空间复杂度：O(n)O(n)，其中 nn 是原数组的长度，最坏情况下，哈希表和原数组等大。
     *
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/degree-of-an-array/solution/shu-zu-de-du-by-leetcode-solution-ig97/
     * @param nums
     * @return
     */
    public int findShortestSubArray_3(int[] nums) {
        Map<Integer, int[]> map = new HashMap<Integer, int[]>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            if (map.containsKey(nums[i])) {
                map.get(nums[i])[0]++;
                map.get(nums[i])[2] = i;
            } else {
                map.put(nums[i], new int[]{1, i, i});
            }
        }
        int maxNum = 0, minLen = 0;
        for (Map.Entry<Integer, int[]> entry : map.entrySet()) {
            int[] arr = entry.getValue();
            if (maxNum < arr[0]) {
                maxNum = arr[0];
                minLen = arr[2] - arr[1] + 1;
            } else if (maxNum == arr[0]) {
                if (minLen > arr[2] - arr[1] + 1) {
                    minLen = arr[2] - arr[1] + 1;
                }
            }
        }
        return minLen;
    }

//    作者：LeetCode-Solution
//    链接：https://leetcode-cn.com/problems/degree-of-an-array/solution/shu-zu-de-du-by-leetcode-solution-ig97/
}
